/**
 * 
 */
package com.googlecode.cswish.model;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;


/**
 * Describe the element information, it call UIBean 'type' or template 'type' to show the context
 * <p>
 * Example:<p>
 * <code>
 * pageElement.setType(TextField.class.getName());<br>
 * pageElement.setType("link.ftl");
 * </code>
 * @author Jerry.Feng Date: 2008-4-8
 */
public class PageElement {

	// some tags has more than one fields, such as combobox
	public final static String EX_NAMES = "exNames";
	public final static String EX_MOCK = "mock";
	
	private String id;
	
	protected String name;
	
	protected String label;
	
	protected String type;
	
	// the class of html element
	protected String clazz;
	
	protected Boolean required;

	protected Object value;
	
	protected Boolean textModel;
	
	@Deprecated
	// TODO: use 'width'
	protected Integer length;
	
	protected Integer colspan = 1;
	
	// show or hidden the element
	protected Boolean hidden;
	
	protected PageElement parent;
	
	protected Map<String, Object> extraParams;
	
	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}
	
	public String getShortName() {
		String shortName;
		if (name.startsWith("_gen_")) {
			// exmaple: _gen_xxx_idyyy ==> yyy
			int endIndex = name.lastIndexOf('_');
			if (endIndex > 0) {
				int beginIndex = name.lastIndexOf('_', endIndex - 1);
				if (beginIndex > 0) {
					shortName = name.substring(beginIndex + 1, endIndex);
				} else {
					shortName = "";
				}
			} else {
				shortName = "";
			}
		} else {
			shortName = name;
		}
		return shortName;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getLabel() {
		return label;
	}

	public void setLabel(String label) {
		this.label = label;
	}
	
	public String getClazz() {
		return clazz;
	}

	public void setClazz(String clazz) {
		this.clazz = clazz;
	}

	public Boolean getRequired() {
		return required;
	}

	public void setRequired(Boolean required) {
		this.required = required;
	}

	public Object getValue() {
		return value;
	}

	public void setValue(Object value) {
		this.value = value;
	}

	public void setTextModel(Boolean textModel) {
		this.textModel = textModel;
	}

	public Boolean getTextModel() {
		if (textModel != null && textModel) {
			return Boolean.TRUE;
		} else {
			return Boolean.FALSE;
		}
	}

	public Integer getLength() {
		return length;
	}

	public void setLength(Integer length) {
		this.length = length;
	}

	public Integer getColspan() {
		return colspan;
	}

	public void setColspan(Integer colspan) {
		this.colspan = colspan;
	}

	public Boolean getHidden() {
		return hidden;
	}

	public void setHidden(Boolean hidden) {
		this.hidden = hidden;
	}
	
	public boolean isVisible() {
		boolean isHidden = "hidden".equals(type) || hidden != null && hidden;
		return !isHidden;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}
	
	public Map<String, Object> getExtraParams() {
		return extraParams;
	}
	
	public Object getExtraParam(String name) {
		return extraParams == null ? null : extraParams.get(name);
	}

	public void setExtraParams(Map<String, Object> extraParams) {
		this.extraParams = extraParams;
	}
	
	/**
	 * Mock means the page is self-defined page, tag uses this flag to generate the '<div>' container
	 * 
	 * @param mock
	 * @return
	 */
	public boolean setMock(boolean mock) {
		boolean oldMock = isMock();
		addExtraParams("mock", mock);
		return oldMock;
	}
	
	public boolean isMock() {
		return Boolean.TRUE.equals(getExtraParam("mock"));
	}
	
	public boolean isDebug() {
		return Boolean.TRUE.equals(getExtraParam("debug"));
	}
	
	public void addElement(String name, PageElement childElement) {
	}
	
	public void addExtraParams(String name, Object value) {
		if (this.extraParams == null) {
			this.extraParams = new HashMap<String, Object>();
		}
		this.extraParams.put(name, value);
	}
	
	public void addExtraName(String exName, String namePrefix, String oldNamePrefix) {
		String fullName;
		if (oldNamePrefix != null && oldNamePrefix.length() > 0) {
			fullName = oldNamePrefix + exName;
		} else if (namePrefix != null && namePrefix.length() > 0) {
			fullName = namePrefix + exName;
		} else {
			fullName = exName;
		}
		
		// TODO: check & add for better performance?
		PageElement element = new PageElement();
		element.setName(fullName);
		element.setType("hidden");
		if (parent != null) {
			parent.addElement(fullName, element);
		} else {
			this.addElement(fullName, element);
		}
	}
	
	public void removeExtraParams(String name) {
		if (this.extraParams != null) {
			this.extraParams.remove(name);
		}
	}
	
	public PageElement getParent() {
		return parent;
	}

	public void setParent(PageElement parent) {
		this.parent = parent;
	}

	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("[name=").append(getName());
		sb.append(" type=").append(getType()).append("]");
		return sb.toString();
	}
	
	public int hashCode() {
		if (getId() != null) {
			return getId().hashCode();
		} else {
			return super.hashCode();
		}
	}
	
	public boolean equals(Object another) {
		if (this.getId() != null && another != null && another instanceof PageElement) {
			PageElement pageElement = (PageElement) another;
			return this.getId().equals(pageElement.getId());
		}
		return false;
	}
	
	public PageElement clone() {
		PageElement cloned = new PageElement();
		cloneTo(cloned);
		return cloned;
	}
	
	protected void cloneTo(PageElement cloned) {
		cloned.setId(getId());
		cloned.setHidden(getHidden());
		cloned.setType(getType());
		cloned.setName(getName());
		cloned.setClazz(getClazz());
		cloned.setLabel(getLabel());
		cloned.setRequired(getRequired());
		cloned.setLength(getLength());
		cloned.setTextModel(getTextModel());
		cloned.setValue(getValue());
		cloned.setColspan(getColspan());
		
		if (getExtraParams() != null) {
			Map<String, Object> extraParams = new HashMap<String, Object>(getExtraParams());
			cloned.setExtraParams(extraParams);
		}
	}

	public void merge(PageElement another) {
		if (getId() == null) {
			setId(another.getId());
		}
		if (getHidden() == null) {
			setHidden(another.getHidden());
		}
		if (getType() == null) {
			setType(another.getType());
		}
		if (getName() == null) {
			setName(another.getName());
		}
		if (getLabel() == null) {
			setLabel(another.getLabel());
		}
		if (getClazz() == null) {
			setClazz(another.getClazz());
		}
		if (getRequired() == null) {
			setRequired(another.getRequired());
		}
		if (getLength() == null) {
			setLength(another.getLength());
		}
		if (getTextModel() == null) {
			setTextModel(another.getTextModel());
		}
		if (getValue() == null) {
			setValue(another.getValue());
		}
		if (getColspan() == null) {
			setColspan(another.getColspan());
		}
		
		if (another.getExtraParams() != null) {
			if (this.getExtraParams() == null) {
				this.extraParams = new HashMap<String, Object>();
				extraParams.putAll(another.getExtraParams());
			} else {
				for (Entry<String, Object> anotherParamEntry : another.getExtraParams().entrySet()) {
					if (extraParams.get(anotherParamEntry.getKey()) == null) {
						extraParams.put(anotherParamEntry.getKey(), anotherParamEntry.getValue());
					}
				}
			}
		}
	}

	protected PageElement deepClone(Map<PageElement, PageElement> identifyMap) {
		return clone();
	}
}